package com.xiaoyushu.user.relation.biz.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSON;
import com.xiaoyushu.framework.biz.context.holder.LoginUserContextHolder;
import com.xiaoyushu.framework.common.exception.BizException;
import com.xiaoyushu.framework.common.response.PageResponse;
import com.xiaoyushu.framework.common.response.Response;
import com.xiaoyushu.framework.common.util.JsonUtils;
import com.xiaoyushu.user.dto.resp.FindUserByIdRspDTO;
import com.xiaoyushu.user.relation.biz.constant.MQConstants;
import com.xiaoyushu.user.relation.biz.constant.RedisKeyConstants;
import com.xiaoyushu.user.relation.biz.domain.dataobject.FansDO;
import com.xiaoyushu.user.relation.biz.domain.dataobject.FollowingDO;
import com.xiaoyushu.user.relation.biz.domain.mapper.FansDOMapper;
import com.xiaoyushu.user.relation.biz.domain.mapper.FollowingDOMapper;
import com.xiaoyushu.user.relation.biz.enums.LuaResuktEnum;
import com.xiaoyushu.user.relation.biz.enums.ResponseCodeEnum;
import com.xiaoyushu.user.relation.biz.model.dto.FollowUserMqDTO;
import com.xiaoyushu.user.relation.biz.model.dto.UnfollowUserMqDTO;
import com.xiaoyushu.user.relation.biz.model.vo.*;
import com.xiaoyushu.user.relation.biz.rpc.UserRpcService;
import com.xiaoyushu.user.relation.biz.service.RelationService;
import com.xiaoyushu.user.relation.biz.util.DateUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;

@Service
@Slf4j
public class RelationServiceImpl implements RelationService {

    @Resource
    private UserRpcService userRpcService;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private FollowingDOMapper followingDOMapper;
    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    @Resource(name = "taskExecutor")
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Autowired
    private FansDOMapper fansDOMapper;

    /**
     * 关注用户
     *
     * @param followUserReqVO
     * @return
     */
    @Override
    public Response<?> follow(FollowUserReqVO followUserReqVO) {
        //关注用户ID
        Long followUserId = followUserReqVO.getFollowUserId();

        //当前登录的用户ID
        Long userId = LoginUserContextHolder.getUserId();
        //校验:无法关注自己
        if (Objects.equals(userId, followUserId)){
            throw new BizException(ResponseCodeEnum.CANT_FOLLOW_YOUR_SELF);
        }
        // TODO :校验关注的用户是否存在
        FindUserByIdRspDTO byId = userRpcService.findById(followUserId);
        if (Objects.isNull(byId)){
            throw new BizException(ResponseCodeEnum.FOLLOW_USER_NOT_EXISTED);
        }
        // 构建当前用户关注列表的Redis Key
        String followingRedisKey=RedisKeyConstants.buildUserFollowingKey(userId);
        log.info("保存缓存的key:{}",followingRedisKey);
        DefaultRedisScript<Long> script=new DefaultRedisScript<>();
        //Lua 脚本路径
        script.setScriptSource(new ResourceScriptSource(new ClassPathResource("/lua/follow_check_and_add.lua")));
        //返回值类型
        script.setResultType(Long.class);
        //当前时间
        LocalDateTime now=LocalDateTime.now();
        //当前时间转时间戳
        Long timestamp=DateUtils.localDateTime2Timestamp(now);
        //执行 Lua脚本拿到返回结果
        Long result=  redisTemplate.execute(script, Collections.singletonList(followingRedisKey),followUserId,timestamp);

        //校验Lua  脚本的结果,根据状态码抛出对应的业务异常
        checkLuaScriptResult(result);
        //ZSET 不存在
        if (Objects.equals(result,LuaResuktEnum.ZSET_NOT_EXIST.getCode())){
            //从数据库拆线呢当前用户的关注关系记录
            List<FollowingDO> followingDOS=followingDOMapper.selectByUserId(userId);
            //随机过期时间
            //保底1天+随机秒数
           long expireSeconds= 60*60*24+ RandomUtil.randomInt(60*60*24);
            //若记录为空,直接ZADD 对象并设置过期时间
            if (CollUtil.isEmpty(followingDOS)){
                DefaultRedisScript<Long> script2= new DefaultRedisScript<>();
                script2.setScriptSource(new ResourceScriptSource(new ClassPathResource("/lua/follow_add_and_expire.lua")));
                script2.setResultType(Long.class);
                // TODO: 可以根据用户类型，设置不同的过期时间，若当前用户为大V, 则可以过期时间设置的长些或者不设置过期时间；如不是，则设置的短些
                // 如何判断呢？可以从计数服务获取用户的粉丝数，目前计数服务还没创建，则暂时采用统一的过期策略
                redisTemplate.execute(script2, Collections.singletonList(followingRedisKey), followUserId, timestamp, expireSeconds);
               // redisTemplate.execute(script2,Collections.singletonList(followingRedisKey),followingDOS,timestamp);
            }else{//若记录不为空,则将关注关系数量全量同步到Redis中,并设置过期时间
                //构建Lua 参数
               Object[] luaArgs= buildLuaArgs(followingDOS,expireSeconds);
                //执行Lua脚本,批量同步关注关系数据到Redis中
                DefaultRedisScript<Long> script3=new DefaultRedisScript<>();
                script3.setScriptSource(new ResourceScriptSource(new ClassPathResource("/lua/follow_batch_add_and_expire.lua")));
                script3.setResultType(Long.class);
                redisTemplate.execute(script3,Collections.singletonList(followingRedisKey),luaArgs);
                //再次调用上面的Lua 脚本:follow_check_and_add.lua,将最新关注关系加进去
                result=redisTemplate.execute(script,Collections.singletonList(followingRedisKey),followUserId,timestamp);
                checkLuaScriptResult(result);
            }
        }

        // 发送 MQ
        //构建消息体 DTO
        FollowUserMqDTO followUserMqDTO = FollowUserMqDTO.builder()
                .userId(userId)
                .followUserId(followUserId)
                .createTime(now)
                .build();
        //构建消息对象,并将DTO转成Json字符串设置到消息提中
        Message<String> message = MessageBuilder.withPayload(JsonUtils.toJsonString(followUserMqDTO))
                .build();
        //通过冒号连接,可让MQ发送给主题 Topic时.携带上标签Tag
       String destination= MQConstants.TOPIC_FOLLOW_OR_UNFOLLOW+ ":" + MQConstants.TAG_FOLLOW;

       log.info("===> 开始发送关注操作 MQ,消息体:{}",followUserMqDTO);
       log.info("==> 发送的message:{}", JSON.toJSON(message));
       //异步发送MQ 消息,提升接口响应速度
        rocketMQTemplate.asyncSend(destination, message, new SendCallback() {
            @Override
            public void onSuccess(SendResult sendResult) {
                log.info("===> MQ发送成功,sendResult:{}",sendResult);
            }

            @Override
            public void onException(Throwable throwable) {
                log.error("==> MQ 发送异常:",throwable);
            }
        });
        return Response.success();
    }

    /**
     * 取关用户
     * @param unfollowUserReqVo 
     * @return
     * 解释一波业务逻辑：
     * •
     * 首先，校验用户是否取关的是自己，不允许取关自己；
     * •
     * 接着校验关注的用户是否存在；
     * •
     * 然后，校验必须是关注了的用户，才能被取关。可通过 ZSCORE 命令来校验，检查被取关用户是否在个人的关注列表中；
     * •
     * 若已在自己的关注列表中，则将其删除；
     * •
     * 构建消息体 DTO，并异步发送 MQ, 让消费者去删除数据库的关系数据，接口直接返回操作成功；
     */
    @Override
    public Response<?> unfollow(UnfollowUserReqVo unfollowUserReqVo) {
        // 想要取关的用户 ID
        Long unfollowUserId = unfollowUserReqVo.getUnfollowUserId();
        //当前登录用户 ID
        Long userId = LoginUserContextHolder.getUserId();
        //无法取关自己
        if (Objects.equals(userId,unfollowUserId)){
            throw new BizException(ResponseCodeEnum.CANT_FOLLOW_YOUR_SELF);
        }
        //校验取关的用户是否存在
        FindUserByIdRspDTO byId = userRpcService.findById(unfollowUserId);
        
        if (Objects.isNull(byId)){
            throw new BizException(ResponseCodeEnum.FOLLOW_USER_NOT_EXISTED);
        }

        //获取当前用户的关注列表 Redis Key
        String followingRedisKey= RedisKeyConstants.buildUserFollowingKey(userId);
        DefaultRedisScript<Long> script = new DefaultRedisScript<>();
        //Lua 脚本路径
        script.setScriptSource(new ResourceScriptSource(new ClassPathResource("/lua/unfollow_check_and_delete.lua")));
        //返回值类型
        script.setResultType(Long.class);

        //执行 Lua脚本,拿到返回结果
        Long result = redisTemplate.execute(script, Collections.singletonList(followingRedisKey), unfollowUserId);

        //校验Lua脚本执行结果
        //取关的用户不在关注列表中
        if (Objects.equals(result,LuaResuktEnum.NOT_FOLLOWED.getCode())){
            throw new BizException(ResponseCodeEnum.NOT_FOLLOWED);
        }
        if (Objects.equals(result,LuaResuktEnum.ZSET_NOT_EXIST.getCode())){
            //从数据库查询当前用户的关注关系记录
            List<FollowingDO> followingDOS = followingDOMapper.selectByUserId(userId);

            //随机过期时间
            //保底1天 + 随机秒数
          long expireSeconds=  60*60*24+RandomUtil.randomInt(60*60*24);
          //若记录为空,则表示还未关注任何人,提示还未关注对方
            if (CollUtil.isEmpty(followingDOS)){
                throw new BizException(ResponseCodeEnum.NOT_FOLLOWED);
            }else{ //若记录不为空,则将关注关系数据数量全同步到 Redis中,并设置过期时间
                // 构建 Lua参数
                Object[] luaArgs = buildLuaArgs(followingDOS, expireSeconds);
                //执行 Lua脚本,批量同步关注关系数据到Redis中
                DefaultRedisScript<Long> script3 = new DefaultRedisScript<>();
                script3.setScriptSource(new ResourceScriptSource(new ClassPathResource("/lua/follow_batch_add_and_expire.lua")));
                script3.setResultType(Long.class);
                redisTemplate.execute(script3,Collections.singletonList(followingRedisKey),luaArgs);

                //再次调用上面的Lua 脚本:unfollow_check_and_delete.lua  将取关的用户删除
              result=  redisTemplate.execute(script,Collections.singletonList(followingRedisKey),unfollowUserId);
                //再次校验结果
                if(Objects.equals(result,LuaResuktEnum.NOT_FOLLOWED.getCode())){
                    throw new BizException(ResponseCodeEnum.NOT_FOLLOWED);
                }

            }

        }
        //发送MQ
        //构建消息体DTO
        UnfollowUserMqDTO unfollowUserMqDTO = UnfollowUserMqDTO.builder()
                .unfollowUserId(unfollowUserId)
                .createTime(LocalDateTime.now())
                .userId(userId)
                .build();
        //构建消息对象,并将DTO 转成 Json 字符串设置到消息体中
        Message<String> message = MessageBuilder.withPayload(JsonUtils.toJsonString(unfollowUserMqDTO))
                .build();

        //通过冒号连接,可让MQ发送主题Topic 时,携带上标签 Tag
        String destination = MQConstants.TOPIC_FOLLOW_OR_UNFOLLOW + ":" + MQConstants.TAG_UNFOLLOW;
        log.info("===> 开始发送取关操作 MQ,消息体:{}",unfollowUserMqDTO);

        String hashKey = String.valueOf(userId);
        //异步发送MQ 消息,提升接口响应速度
        rocketMQTemplate.asyncSendOrderly(destination, message,hashKey, new SendCallback() {
            @Override
            public void onSuccess(SendResult sendResult) {
                log.info("===> MQ 发送成功,SendResult:{}",sendResult);
            }

            @Override
            public void onException(Throwable throwable) {
                log.error("===> MQ 发送异常:",throwable);
            }
        });

        return Response.success();
    }

    /**
     * @param findFollowingListReqVO
     * @return
     */
    @Override
    public PageResponse<FindFollowingListRspVO> findFollowingList(FindFollowingListReqVO findFollowingListReqVO) {
       //想要查询的用户 ID
        Long userId=findFollowingListReqVO.getUserId();
       //页码
       Integer pageNo= findFollowingListReqVO.getPageNo();

       //先从 Redis 中查询
        String followingListRedisKey=RedisKeyConstants.buildUserFollowingKey(userId);

        //查询目标用户关注列表 ZSet的总大小
        long total=redisTemplate.opsForZSet().zCard(followingListRedisKey);

        //返参
        List<FindFollowingListRspVO> findFollowingListRspVOS=null;
        //每页展示 10条数据
        long limit =10;
        if (total>0){ //缓存中有数据

            //计算一共多少页
            long totalPage= PageResponse.getTotalPage(total,limit);

            //请求的页面超过了总页数
            if (pageNo > totalPage) return PageResponse.success(null,pageNo,total);

            //准备从Redis 中查询ZSet分页数据
            // 每页10 个元素,计算偏移量
            long offset=(pageNo-1)*limit;

            //使用 zrevrangebyscore 命令按score 降序获取元素,同时使用 Limit自居实现分页
            // 注意:这里使用了Double.positive_infinity 和 Double.negative_infinity作为分数范围
            //因为关注列表最多有 1000个元素,这样可以确保获取到所有的元素
            Set<Object> followingUserIdsSet= redisTemplate.opsForZSet()
                    .reverseRangeByScore(followingListRedisKey,Double.NEGATIVE_INFINITY
                            ,Double.POSITIVE_INFINITY,offset,limit);

            if (CollUtil.isNotEmpty(followingUserIdsSet)){
                //提取所有用户 ID 到集合中
              List<Long>  userIds= followingUserIdsSet.stream().map(object -> Long.valueOf(object.toString())).toList();
              //RPC:批量查询用户信息
                List<FindUserByIdRspDTO> findUserByIdRspDTOS= userRpcService.findByIds(userIds);
              //若不为空,DTO 转 VO
                if (CollUtil.isNotEmpty(findUserByIdRspDTOS)){
                    findFollowingListRspVOS=findUserByIdRspDTOS.stream()
                            .map(dto->FindFollowingListRspVO.builder()
                                    .userId(dto.getId())
                                    .avatar(dto.getAvatar())
                                    .nickname(dto.getNickName())
                                    .introduction(dto.getIntroduction())
                                    .build())
                            .toList();
                }
            }
        }else {
            // 若Redis中 没有数据,则从数据库查询
            //先查询记录总量
            long  count=followingDOMapper.selectCountByUserID(userId);

            //计算一共多少页
            long totalPage=PageResponse.getTotalPage(count,limit);

            // 请求的页面超过了总页数
            if (pageNo>totalPage) return PageResponse.success(null,pageNo,count);

            //偏移量
            long offset=PageResponse.getOffset(pageNo,limit);

            //分页查询
          List<FollowingDO> followingDOS=  followingDOMapper.selectPageListByUserId(userId,offset,limit);
          //赋值真实的记录总数
          total=count;

          //若记录不为空
            if (CollUtil.isNotEmpty(followingDOS)){
                //提取所有用户ID到集合中
                List<Long>  userIds=followingDOS.stream().map(FollowingDO::getFollowingUserId).toList();

                //RPC:调用用户服务,将DTO转换为 VO
               findFollowingListRspVOS=rpcUserServiceAndDTO2VO(userIds,findFollowingListRspVOS);

                //TODO :异步将关注列表全量同步到Redis
                threadPoolTaskExecutor.submit(() -> syscFollowingList2Redis(userId));

            }


        }

        return PageResponse.success(findFollowingListRspVOS,pageNo,total);
    }

    /**
     * 查询关注列表
     * 查询粉丝用户列表
     *
     * @param findFansListReqVo
     * @return
     */
    @Override
    public PageResponse<FindFansUserRspBVO> findFansList(FindFansListReqVo findFansListReqVo) {
       //想要查询的用户 ID
        Long userId = findFansListReqVo.getUserId();
        //页码
        Integer pageNo = findFansListReqVo.getPageNo();

        //先从Redis 中查询
        String fansListRedisKey = RedisKeyConstants.buildUserFansKey(userId);

        //查询目标用户粉丝列表 ZSet 的总大小
        Long total = redisTemplate.opsForZSet().zCard(fansListRedisKey);

        //反参
        List<FindFansUserRspBVO> findFansUserRspBVOS =null;

        //每页展示10条数据
        long limit = 10;
        if (total > 0){ //缓存中有数据
            //计算一共多少页
            long totalPage =PageResponse.getTotalPage(total,limit);

            //请求 的页面超出了总页数
            if(pageNo > totalPage )return PageResponse.success(null,pageNo,total);

            //准备从Redis 中查询ZSet 分页数据
            //每页 10个元素,计算偏移量
            long offset = PageResponse.getOffset(pageNo, limit);

            //使用 ZREVRANGEBYSCORE 命令按 score降序获取元素,同时使用LIMIT 子句实现分页
            Set<Object> followingUserIdsSet = redisTemplate.opsForZSet()
                    .reverseRangeByScore(fansListRedisKey, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, offset, limit);

            if (CollUtil.isNotEmpty(followingUserIdsSet)){
                //提取所有用户 ID 到集合中
                List<Long> userIds = followingUserIdsSet.stream().map(object -> Long.valueOf(object.toString())).toList();
                
                //RPC:批量查询用户信息
                findFansUserRspBVOS=rpcUserServiceAndCountServiceAndDTO2VO(userIds,findFansUserRspBVOS);
            }
        } else{ //若Redis缓存中无数据,则查询数据库
            //先拆线呢记录总量
            total =fansDOMapper.selectCountByUserId(userId);

            //计算一共多少页
            long totalPage =PageResponse.getTotalPage(total,limit);

            //请求的页面超出了总页数(只运行查询前500页)
            if (pageNo >500 || pageNo > totalPage ) return PageResponse.success(null,pageNo,total);

            //偏移量
            long offset =PageResponse.getOffset(pageNo,limit);

            //分页查询
            List<FansDO> fansDOS = fansDOMapper.selectPageListByUSerID(userId, offset, limit);

            //若记录不为空
            if (CollUtil.isNotEmpty(fansDOS)){
                //提取所有粉丝用户 ID到集合中
                List<Long> userIds= fansDOS.stream().map(FansDO::getFansUserId).toList();

                //RPC:调用用户服务\计数服务,并将DTO转换为VO
                findFansUserRspBVOS=rpcUserServiceAndCountServiceAndDTO2VO(userIds,findFansUserRspBVOS);

                //异步将粉丝列表同步到 Redis (最多 5000条)
                threadPoolTaskExecutor.submit(()-> syscFansList2Redis(userId));
            }

        }

        return PageResponse.success(findFansUserRspBVOS,pageNo,total);
    }

    /**
     * 粉丝列表同步到 Redis (最多 5000 条)
     * @param userId
     * @return
     */
    private void syscFansList2Redis(Long userId) {
        //查询粉丝列表(最多 5000位用户)
        List<FansDO> fansDOS = fansDOMapper.select5000FansByUserID(userId);
        if(CollUtil.isNotEmpty(fansDOS)){
            //用户粉丝列表 Redis Key
            String fansListRedisKey = RedisKeyConstants.buildUserFansKey(userId);

            //随机过期时间
            //保底1天 +随机秒数
            long expireSeconds=60*60*24 + RandomUtil.randomInt(60*60*24);
            //构建 Lua 参数
           Object[] luaArgs=  buildFansZSetLuaArgs(fansDOS,expireSeconds);

           //执行 Lua 脚本,批量同步关注关系数据到 Redis中
            DefaultRedisScript<Long> script = new DefaultRedisScript<>();
            script.setScriptSource(new ResourceScriptSource(new ClassPathResource("/lua/follow_batch_add_and_expire.lua")));
            script.setResultType(Long.class);
            redisTemplate.execute(script,Collections.singletonList(fansListRedisKey),luaArgs);

        }

    }

    private Object[] buildFansZSetLuaArgs(List<FansDO> fansDOS, long expireSeconds) {
        int argsLength =fansDOS.size()*2+1;//每个粉丝关系有2个参数 (score 和value), 再加一个过期时间
        Object[] luaArgs=new Object[argsLength];
        int i=0;
        for (FansDO fansDO:fansDOS){
            luaArgs[i]=DateUtils.localDateTime2Timestamp(fansDO.getCreateTime());
            luaArgs[i+1] =fansDO.getFansUserId(); //粉丝的用户ID 作为ZSet value
            i+=2;
        }
        luaArgs[argsLength-1]=expireSeconds;//最后一个参数是 ZSet 的过期时间
        return luaArgs;
    }

    /**
     * RPC: 调用用户服务\计数服务,并将DTO转换为 VO粉丝列表
     * @param userIds
     * @param findFansUserRspBVOS
     * @return
     */

    private List<FindFansUserRspBVO> rpcUserServiceAndCountServiceAndDTO2VO(List<Long> userIds, List<FindFansUserRspBVO> findFansUserRspBVOS) {
        // RPC:批量查询用户信息
        List<FindUserByIdRspDTO> findUserByIdRspDTOS = userRpcService.findByIds(userIds);

        //TODO RPC:批量查询用户的计数数据 (笔记总数\粉丝总数)

        //若不为空,DTO转VO
        if (CollUtil.isNotEmpty(findUserByIdRspDTOS)){
            findFansUserRspBVOS=findUserByIdRspDTOS.stream()
                    .map(dto ->FindFansUserRspBVO.builder()
                            .userId(dto.getId())
                            .avatar(dto.getAvatar())
                            .nickname(dto.getNickName())
                            .noteTotal(0L)//TODO: 这块数据暂无,后续补充
                            .fansTotal(0L)//TODO: 这块数据暂无,后续补充
                            .build())
                    .toList();

        }
        return findFansUserRspBVOS;

    }

    /**
     * 全量同步关注列表至 Redis中
     * @param userId
     * @return
     */

    private Object syscFollowingList2Redis(Long userId) {
        //查询全量关注用户列表 (1000 位用户)
        List<FollowingDO> followingDOS= followingDOMapper.selectAllByUserId(userId);
        if (CollUtil.isNotEmpty(followingDOS)){
            //用户关注列表 Redis Key
            String followingListRedisKey = RedisKeyConstants.buildUserFollowingKey(userId);
            //随机过期时间
            //保底1天+随机秒数
            long expireSeconds=60*60*24 +RandomUtil.randomInt(60*60*24);
            //构建Lua 参数
            Object[] luaArgs = buildLuaArgs(followingDOS, expireSeconds);

            //执行Lua脚本,批量同步关注关系数据到Redis中
            DefaultRedisScript<Long> script = new DefaultRedisScript<>();
            script.setScriptSource(new ResourceScriptSource(new ClassPathResource("/lua/follow_batch_add_and_expire.lua")));
            script.setResultType(Long.class);
            redisTemplate.execute(script,Collections.singletonList(followingListRedisKey),luaArgs);

        }


        return null;
    }

    /**
     * RPC 调用用户服务,并将DTO转换为VO
     * @param userIds
     * @param findFollowingListRspVOS
     * @return
     */
    private List<FindFollowingListRspVO> rpcUserServiceAndDTO2VO(List<Long> userIds, List<FindFollowingListRspVO> findFollowingListRspVOS) {
        //RPC 批量查询用户信息
        List<FindUserByIdRspDTO> findUserByIdRspDTOS=userRpcService.findByIds(userIds);
        //若不为空,DTO转 VO
        if (CollUtil.isNotEmpty(findUserByIdRspDTOS)){
            findFollowingListRspVOS=findUserByIdRspDTOS.stream()
                    .map(dto->FindFollowingListRspVO.builder()
                            .userId(dto.getId())
                            .avatar(dto.getAvatar())
                            .nickname(dto.getNickName())
                            .introduction(dto.getIntroduction())
                            .build())
                    .toList();
        }
        return findFollowingListRspVOS;
    }

    /**
     * 构建lua脚本参数
     * @param followingDOS
     * @param expireSeconds
     * @return
     */
    private Object[] buildLuaArgs(List<FollowingDO> followingDOS, long expireSeconds) {
        int argsLength = followingDOS.size() * 2 + 1; // 每个关注关系有 2 个参数（score 和 value），再加一个过期时间
        Object[] luaArgs = new Object[argsLength];

        int i = 0;
        for (FollowingDO following : followingDOS) {
            luaArgs[i] = DateUtils.localDateTime2Timestamp(following.getCreateTime()); // 关注时间作为 score
            luaArgs[i + 1] = following.getFollowingUserId();          // 关注的用户 ID 作为 ZSet value
            i += 2;
        }

        luaArgs[argsLength - 1] = expireSeconds; // 最后一个参数是 ZSet 的过期时间
        return luaArgs;
    }

    /**
     * 校验Lua  脚本的结果,根据状态码抛出对应的业务异常
     * @param result
     */
    private void checkLuaScriptResult(Long result) {
        log.info("是否存在:{}",result);
        LuaResuktEnum luaResuktEnum=LuaResuktEnum.valueOf(result);
        if (Objects.isNull(luaResuktEnum)) throw new RuntimeException("Lua 返回结果错误");

        //判断返回结果
        switch (luaResuktEnum){
            //关注数已经达到上限
            case FOLLOW_LIMIT -> throw new BizException(ResponseCodeEnum.FOLLOWING_COUNT_LIMIT);
            //已经关注了该用户
            case ALREADY_FOLLOWED -> throw new BizException(ResponseCodeEnum.ALREADY_FOLLOWED);
        }
    }
}
